/************************************************************
** @brief   : epd_paint
** @author  : vandoul
** @github  : https://gitee.com/vandoul
** @date    : 2022-01
** @version : v1.0.0
** @note    : epd_paint.c
***********************************************************/

#include "epd_paint.h"
#include "drv_epd.h"

struct _epd_paint {
    uint8_t *buf;
    int width;
    int height;
    int rotate;
    bool invert_color;
};

void epd_paint_init(struct _epd_paint *paint, uint8_t *buf, int w, int h)
{
    if(paint == RT_NULL) {
        return ;
    }
    paint->buf = buf;
    paint->height = h;
    paint->width = w;
    paint->rotate = EPD_PAINT_ROTATE_0;
    paint->invert_color = true;
}

epd_paint_t epd_paint_create(uint8_t *buf, int w, int h)
{
    epd_paint_t paint;
    paint = rt_malloc(sizeof(struct _epd_paint));
    if(paint == RT_NULL) {
        return RT_NULL;
    }
    epd_paint_init(paint, buf, w, h);
    return paint;
}

void epd_paint_delete(epd_paint_t paint)
{
    rt_free(paint);
}

static inline int epd_paint_check_rotate_0_or_180(epd_paint_t paint, int x, int y)
{
    if(x < 0 || x >= paint->width || y < 0 || y >= paint->height) {
        return 0;
    }
    return 1;
}
static inline int epd_paint_check_rotate_90_or_270(epd_paint_t paint, int x, int y)
{
    if(x < 0 || x >= paint->height || y < 0 || y >= paint->width) {
        return 0;
    }
    return 1;
}

void epd_paint_draw_absolute_pixel(epd_paint_t paint, int x, int y, int colored)
{
    if(!epd_paint_check_rotate_0_or_180(paint, x, y)) {
        return ;
    }
    if((paint->invert_color && colored) || (!(paint->invert_color || colored))) {
        paint->buf[(x + y * paint->width) >> 3] |= 0x80 >> (x & 7);
    } else {
        paint->buf[(x + y * paint->width) >> 3] &= ~(0x80 >> (x & 7));
    }
}

void epd_paint_clear(epd_paint_t paint, int colored)
{
    for(int x = 0; x < paint->width; x ++) {
        for(int y = 0; y < paint->height; y++) {
            epd_paint_draw_absolute_pixel(paint, x, y, colored);
        }
    }
}

uint8_t *epd_paint_get_image(epd_paint_t paint)
{
    return paint->buf;
}

int epd_paint_get_width(epd_paint_t paint)
{
    return paint->width;
}

void epd_paint_set_width(epd_paint_t paint, int w)
{
    paint->width = w&7?(w+8)&(~7):w;
}

int epd_paint_get_height(epd_paint_t paint)
{
    return paint->height;
}

void epd_paint_set_height(epd_paint_t paint, int h)
{
    paint->height = h;
}

int epd_paint_get_rotate(epd_paint_t paint)
{
    return paint->rotate;
}

void epd_paint_set_rotate(epd_paint_t paint, int r)
{
    paint->rotate = r;
}

int epd_paint_get_invert_color(epd_paint_t paint)
{
    return paint->invert_color;
}

void epd_paint_set_invert_color(epd_paint_t paint, bool invert)
{
    paint->invert_color = invert;
}

void epd_paint_draw_pixel(epd_paint_t paint, int x, int y, int colored)
{
    if(paint->rotate == EPD_PAINT_ROTATE_0) {
        if(!epd_paint_check_rotate_0_or_180(paint, x, y)) {
            return ;
        }
        epd_paint_draw_absolute_pixel(paint, x, y, colored);
    } else if(paint->rotate == EPD_PAINT_ROTATE_90){
        if(!epd_paint_check_rotate_90_or_270(paint, x, y)) {
            return ;
        }
        epd_paint_draw_absolute_pixel(paint, paint->width - y, x, colored);
    } else if(paint->rotate == EPD_PAINT_ROTATE_180){
        if(!epd_paint_check_rotate_0_or_180(paint, x, y)) {
            return ;
        }
        epd_paint_draw_absolute_pixel(paint, paint->width - x, paint->height - y, colored);
    } else if(paint->rotate == EPD_PAINT_ROTATE_270){
        if(!epd_paint_check_rotate_90_or_270(paint, x, y)) {
            return ;
        }
        epd_paint_draw_absolute_pixel(paint, y, paint->height - x, colored);
    }
}

void epd_paint_draw_char_at(epd_paint_t paint, int x, int y, char ascii_char, struct epd_font *font, int colored)
{
    int32_t char_offset = ascii_char - ' ';
    if(char_offset < 0) {
        char_offset = 0;
    }
    char_offset = char_offset * font->height * ((font->width >> 3) + (font->width & 7 ? 1 : 0));
    const uint8_t *ptr = &font->table[char_offset];

    for(int j=0; j < font->height; j++) {
        for(int i = 0; i < font->width; i++) {
            if((*ptr) & (0x80 >> (i & 7))) {
                epd_paint_draw_pixel(paint, x + i, y + j, colored);
            } else {
                epd_paint_draw_pixel(paint, x + i, y + j, !colored);
            }
            if((i & 7) == 7) {
                ptr ++;
            }
        }
        if((font->width & 7) != 0) {
            ptr ++;
        }
    }
}

void epd_paint_draw_string_at(epd_paint_t paint, int x, int y, const char *text, struct epd_font *font, int align, int colored)
{
    const char *p_text = text;
    int refcolumn;
    if(align == EPD_PAINT_ALIGN_LEFT) {
        refcolumn = x;
    } else if(align == EPD_PAINT_ALIGN_RIGHT) {
        refcolumn = x - font->width * rt_strlen(text);
    } else if(align == EPD_PAINT_ALIGN_CENTER) {
        refcolumn = x - font->width * rt_strlen(text) / 2;
    } else {
        return ;
    }
//    rt_kprintf("string at %d, %d\r\n", refcolumn, y);

    while(*p_text != 0) {
        epd_paint_draw_char_at(paint, refcolumn, y, *p_text, font, colored);
        refcolumn += font->width;
        p_text ++;
    }
}

void epd_paint_draw_string_at_with_invert(epd_paint_t paint, int x, int y, const char *text, struct epd_font *font, int align, int colored, int begin, int end)
{
    int index = 0;
    const char *p_text = text;
    int refcolumn;
    if(align == EPD_PAINT_ALIGN_LEFT) {
        refcolumn = x;
    } else if(align == EPD_PAINT_ALIGN_RIGHT) {
        refcolumn = x - font->width * rt_strlen(text);
    } else if(align == EPD_PAINT_ALIGN_CENTER) {
        refcolumn = x - font->width * rt_strlen(text) / 2;
    } else {
        return ;
    }
    while(p_text[index] != 0) {
        if(index >= begin && index < end) {
            epd_paint_draw_char_at(paint, refcolumn, y, p_text[index], font, !colored);
        } else {
            epd_paint_draw_char_at(paint, refcolumn, y, p_text[index], font, colored);
        }
        refcolumn += font->width;
        index ++;
    }
}

void epd_paint_draw_line(epd_paint_t paint, int x0, int y0, int x1, int y1, int colored)
{
    int dx = x1 - x0 >= 0 ? x1 - x0 : x0 - x1;
    int sx = x0 < x1 ? 1 : -1;
    int dy = y1 - y0 <= 0 ? y1 - y0 : y0 - y1;
    int sy = y0 < y1 ? 1 : -1;
    int err = dx + dy;

    while((x0 != x1) && (y0 != y1)) {
        epd_paint_draw_pixel(paint, x0, y0, colored);
        if(2 * err >= dy) {
            err += dy;
            x0 += sx;
        }
        if(2 * err <= dx) {
            err += dx;
            y0 += sy;
        }
    }
}

void epd_paint_draw_horizontal_line(epd_paint_t paint, int x, int y, int line_width, int colored)
{
    for(int i = x; i < x + line_width; i++) {
        epd_paint_draw_pixel(paint, i, y, colored);
    }
}

void epd_paint_draw_vertical_line(epd_paint_t paint, int x, int y, int line_height, int colored)
{
    for(int i = y; i < y + line_height; i++) {
        epd_paint_draw_pixel(paint, x, i, colored);
    }
}

void epd_paint_draw_rectangle(epd_paint_t paint, int x0, int y0, int x1, int y1, int colored)
{
    int min_x, min_y, max_x, max_y;
    min_x = x1 > x0 ? x0 : x1;
    max_x = x1 > x0 ? x1 : x0;
    min_y = y1 > y0 ? y0 : y1;
    max_y = y1 > y0 ? y1 : y0;

    epd_paint_draw_horizontal_line(paint, min_x, min_y, max_x - min_x + 1, colored);
    epd_paint_draw_horizontal_line(paint, min_x, max_y, max_x - min_x + 1, colored);
    epd_paint_draw_vertical_line(paint, min_x, min_y, max_y - min_y + 1, colored);
    epd_paint_draw_vertical_line(paint, max_x, min_y, max_y - min_y + 1, colored);
}

void epd_paint_fill_rectangle(epd_paint_t paint, int x0, int y0, int x1, int y1, int colored)
{
    int min_x, min_y, max_x, max_y;
    min_x = x1 > x0 ? x0 : x1;
    max_x = x1 > x0 ? x1 : x0;
    min_y = y1 > y0 ? y0 : y1;
    max_y = y1 > y0 ? y1 : y0;

    for(int i = min_x; i <= max_x; i++) {
        epd_paint_draw_vertical_line(paint, i, min_y, max_y - min_y + 1, colored);
    }
}

void epd_paint_draw_circle(epd_paint_t paint, int x, int y, int radius, int colored)
{
    int x_pos = -radius;
    int y_pos = 0;
    int err = 2 - 2 * radius;
    int e2;

    do {
        epd_paint_draw_pixel(paint, x - x_pos, y + y_pos, colored);
        epd_paint_draw_pixel(paint, x + x_pos, y + y_pos, colored);
        epd_paint_draw_pixel(paint, x + x_pos, y - y_pos, colored);
        epd_paint_draw_pixel(paint, x - x_pos, y - y_pos, colored);
        e2 = err;
        if(e2 <= y_pos) {
            err += ++y_pos * 2 + 1;
            if(-x_pos == y_pos && e2 <= x_pos) {
                e2 = 0;
            }
        }
        if(e2 > x_pos) {
            err += ++x_pos * 2 + 1;
        }
    } while(x_pos <= 0);
}

void epd_paint_fill_circle(epd_paint_t paint, int x, int y, int radius, int colored)
{
    int x_pos = -radius;
    int y_pos = 0;
    int err = 2 - 2 * radius;
    int e2;

    do {
        epd_paint_draw_pixel(paint, x - x_pos, y + y_pos, colored);
        epd_paint_draw_pixel(paint, x + x_pos, y + y_pos, colored);
        epd_paint_draw_pixel(paint, x + x_pos, y - y_pos, colored);
        epd_paint_draw_pixel(paint, x - x_pos, y - y_pos, colored);
        epd_paint_draw_horizontal_line(paint, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored);
        epd_paint_draw_horizontal_line(paint, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored);
        e2 = err;
        if(e2 <= y_pos) {
            err += ++y_pos * 2 + 1;
            if(-x_pos == y_pos && e2 <= x_pos) {
                e2 = 0;
            }
        }
        if(e2 > x_pos) {
            err += ++x_pos * 2 + 1;
        }
    } while(x_pos <= 0);
}


